/*
 * Copyright 2009-2009 Tanesha Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 *    1. Redistributions of source code must retain the above copyright notice, 
 *    	 this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *    	 this list of conditions and the following disclaimer in the documentation 
 *    	 and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *  OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are those 
 * of the authors and should not be interpreted as representing official policies, 
 * either expressed or implied, of the Tanesha Project.
 */
package com.google.api.chart;

/**
 * Represents the colors available.
 * 
 * @author sorenad
 */
public final class ColorMaker {

	// contrast black.white
	/**
	 * Black color 000000
	 */
	public static ColorMaker BLACK = ColorMaker.rrggbb("000000");
	/**
	 * White color ffffff
	 */
	public static ColorMaker WHITE = ColorMaker.rrggbb("ffffff");

	// primary colors
	public static ColorMaker RED = ColorMaker.rrggbb("ff0000");
	public static ColorMaker GREEN = ColorMaker.rrggbb("00ff00");
	public static ColorMaker BLUE = ColorMaker.rrggbb("0000ff");
	// secondary colors
	public static ColorMaker YELLOW = ColorMaker.rrggbb("ffff00");
	public static ColorMaker CYAN = ColorMaker.rrggbb("00ffff");
	public static ColorMaker FUCHSIA = ColorMaker.rrggbb("ff00ff");

	// no color
	public static ColorMaker EMPTY = new ColorMaker();
	
	public static ColorMaker rrggbb(String rrggbb) {
		return rrggbb(rrggbb, null);
	}

	public static ColorMaker rrggbb(String rrggbb, String transparency) {
		// not rrggbb value
		if (rrggbb == null || rrggbb.length() != 6)
			throw new IllegalArgumentException("rrggbb colors must be length 6");
		
		int r = Integer.parseInt(rrggbb.substring(0, 2), 16);
		int g = Integer.parseInt(rrggbb.substring(2, 4), 16);
		int b = Integer.parseInt(rrggbb.substring(4, 6), 16);
		Integer t = (transparency == null ? null : Integer.parseInt(transparency, 16));
		
		return new ColorMaker(r, g, b, t);
	}

	public static ColorMaker rgb(int r, int g, int b) {
		return new ColorMaker(r, g, b, null);
	}

	private String rrggbb = null;
	private Integer transparency = null;
	private int r;
	private int g;
	private int b;
	
	private ColorMaker() {
		this.rrggbb = "";
	}

	private ColorMaker(int r, int g, int b, Integer transparency) {
		this.r = r;
		this.g = g;
		this.b = b;
		this.transparency = transparency;
		fixRanges();
	}
	
	
	/**
	 * Sets the transparency of a color.
	 * @param d A value in [0, 1]. 1 not transparent, 0 full transparency
	 * @return
	 */
	public ColorMaker transparency(double d) {
		if (d < 0.0 || d > 1.0)
			throw new IllegalArgumentException("Transparency must be within [0, 1]");
		return transparency((int) (255 * d));
	}
	
	public ColorMaker transparency(String s) {
		return new ColorMaker(r, g, b, Integer.parseInt(s, 16));
	}

	public ColorMaker transparency(int i) {
		if (i < 0 || i > 255)
			throw new IllegalArgumentException("Transparency must be within [0, 255]");
		return transparency(String.format("%02x", i));
	}

	private void fixRanges() {
		if (r > 255)
			r = 255;
		if (r < 0)
			r = 0;
		if (g > 255)
			g = 255;
		if (g < 0)
			g = 0;
		if (b > 255)
			b = 255;
		if (b < 0)
			b = 0;
		if (transparency != null) {
			if (transparency > 255)
				transparency = 255;
			if (transparency < 0)
				transparency = 0;
		}
	}
	
	/**
	 * Creates a new color by adding this and another color. The transparency
	 * of "this" color is preserved in the new color.
	 * @param color The other color to add
	 * @return
	 */
	public ColorMaker additive(ColorMaker color) {
		return new ColorMaker(r + color.r, g + color.g, b + color.b, transparency);
	}

	/**
	 * Creates a new color by subtracting another color from this color. The transparency
	 * of "this" color is preserved in the new color.
	 * @param color The other color to subtract
	 * @return
	 */
	public ColorMaker subtractive(ColorMaker color) {
		return new ColorMaker(r - color.r, g - color.g, b - color.b, transparency);
	}

	/**
	 * Creates a new color by max merging another color with this color. The transparency
	 * of "this" color is preserved in the new color.
	 * @param color The other color to merge with
	 * @return
	 */
	public ColorMaker maxMerge(ColorMaker color) {
		return new ColorMaker(Math.max(r, color.r), Math.max(g, color.g), Math.max(b, color.b), transparency);
	}

	/**
	 * Creates a new color by average merging another color with this color. The transparency
	 * of "this" color is preserved in the new color.
	 * @param color The other color to merge with
	 * @return
	 */
	public ColorMaker avgMerge(ColorMaker color) {
		return new ColorMaker((r + color.r) / 2,(g + color.g) / 2, (b + color.b) / 2, transparency);
	}

	/**
	 * Creates a new color by min merging another color with this color. The transparency
	 * of "this" color is preserved in the new color.
	 * @param color The other color to merge with
	 * @return
	 */
	public ColorMaker minMerge(ColorMaker color) {
		return new ColorMaker(Math.min(r, color.r), Math.min(g, color.g), Math.min(b, color.b), transparency);
	}
	
	/**
	 * Creates a new darker color by adjusting all color levels to the darker side
	 * @return
	 */
	public ColorMaker darker() {
		return adjust(-10);
	}

	/**
	 * Creates a new darker color by adjusting all color levels to the darker side
	 * @param val how much darker [0, 255]
	 * @return
	 */
	public ColorMaker darker(int val) {
		return adjust(-val);
	}

	/**
	 * Creates a new lighter color by adjusting all color levels to the lighter side
	 * @return
	 */
	public ColorMaker lighter() {
		return adjust(10);
	}

	/**
	 * Creates a new lighter color by adjusting all color levels to the lighter side
	 * @param i how much lighter [0, 255]
	 * @return
	 */
	public ColorMaker lighter(int i) {
		return adjust(i);
	}

	private ColorMaker adjust(int val) {
		return new ColorMaker(r + val, g + val, b + val, transparency);
	}

	@Override
	public String toString() {
		if (rrggbb != null)
			return rrggbb;
		else
			return (String.format("%02x%02x%02x", r, g, b) + (transparency == null ? "" : String.format("%02x", transparency))).toUpperCase();
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		else if (!(obj instanceof ColorMaker))
			return false;
		else {
			return this.toString().equals(obj.toString());
		}
	}
}
